//-----------------------------------------------------------------------
// <copyright file="Login.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Barry Dorrans</author>
// <email>barryd@charteris.com</email>
// <date>2005-07-18</date>
// <summary>Build task to login as a different user</summary>
//-----------------------------------------------------------------------


namespace Microsoft.Sdc.Tasks.Security
{
  using System;
  using System.Collections.Generic;
  using System.Text;

  using Microsoft.Build.Tasks;
  using Microsoft.Build.Framework;
  using Microsoft.Build.Utilities;

  using System.ComponentModel;
  using System.Runtime.InteropServices;
  using System.Security.Principal;
  using System.Security.Permissions;
  
  /// <summary>
  /// Logs in using the specified login name and password
  /// </summary>
  /// <remarks>
  /// <code><![CDATA[<Security.Login UserName="barryd" Password="sekrit" />]]></code>
  /// <para>where:</para>
  /// <para><i>UserName (Required)</i></para>
  /// <para>The username to login as.</para>
  /// <para><i>Password (Required)</i></para>
  /// <para>The password for the specified username.</para>
  /// <para>To logout use the <see cref="Microsoft.Sdc.Tasks.Security.Logout"/> task.</para>
  /// </remarks>
  public class Login : TaskBase
  {
    private string loginID;
    private string loginPassword;
    private string loginDomain = System.Environment.MachineName;
    private login32Type type = login32Type.Interactive;
    private login32Provider provider = login32Provider.Default;

    /// <summary>
    /// Initialises a new instance of the Login class. 
    /// </summary>
    public Login()
    { 
    }

    /// <summary>
    /// Performs the action of this task.
    /// </summary>
    protected override void InternalExecute()
    {
      IntPtr userHandle = new IntPtr(0);
      bool returnValue = NativeMethods.LogonUser(loginID,
          loginDomain,
          loginPassword,
          (int)type,
          (int)provider,
          ref userHandle);

      if (!returnValue)
      {
        int win32Error = Marshal.GetLastWin32Error();
        throw new TaskException(
          Log.FormatResourceString("Security.Login.Failed", type.ToString(), loginDomain, loginID));
      }
      WindowsIdentity newId = new WindowsIdentity(userHandle);
      WindowsImpersonationContext impersonatedUser = newId.Impersonate();
      contextStacks.impersonationContexts.Push(impersonatedUser);
      contextStacks.processHandles.Push(userHandle);
      Log.LogWarningFromResources("Security.Login.RunningAs", WindowsIdentity.GetCurrent().Name);
    
    }


    /// <summary>
    /// The username to login as.
    /// </summary>
    /// <value>The username to login as.</value>
    [Required]
    public string UserName
    {
      get { return loginID; }
      set 
      {
        if (value.Contains(@"\"))
        {
          loginDomain = value.Substring(0, value.IndexOf(@"\"));
          loginID = value.Substring(value.IndexOf(@"\") + 1);
        }
        else
          loginID = value; 
      }
    }

    /// <summary>
    /// The password for the specified <see cref="UserName"/>
    /// </summary>
    /// <value>The password for the specified <see cref="UserName"/></value>
    [Required]
    public string Password
    {
      get { return loginPassword; }
      set { loginPassword = value; }
    }

    /// <summary>
    /// The domain to use when logging in.
    /// </summary>
    /// <value>The domain to use when logging in.</value>
    /// <remarks>This defaults to the local machine name.</remarks>
    public string Domain
    {
      get { return loginDomain; }
      set { loginDomain = value; }
    }

    /// <summary>
    /// The <see cref="login32Type"/>  to login as.
    /// </summary>
    /// <value>The <see cref="login32Type"/> to login as</value>
    public string LoginType
    {
      get { return type.ToString(); }
      set {
        type = (login32Type)Enum.Parse(typeof(login32Type), (string)value, true); 
      }
    }

    /// <summary>
    /// The <see cref="login32Provider"/> to use.
    /// </summary>
    /// <value>The <see cref="login32Provider"/> to use.</value>
    public string LoginProvider
    {
      get { return provider.ToString(); }
      set { provider= (login32Provider) Enum.Parse(typeof(login32Provider), (string)value, true); }
    }

    /// <summary>
    /// Imported Win32 API functions.
    /// </summary>
    internal class NativeMethods
    {
      [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
      public static extern bool LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
    }

    /// <summary>
    /// Login Types
    /// </summary>
    internal enum login32Type
    {
      /// <summary>
      /// This logon type is intended for users who will be interactively using the computer, such as a user being logged on
      /// by a terminal server, remote shell, or similar process.
      /// This logon type has the additional expense of caching logon information for disconnected operations;
      /// therefore, it is inappropriate for some client/server applications,
      /// such as a mail server.
      /// </summary>
      Interactive = 2,
      /// <summary>
      /// This logon type is intended for high performance servers to authenticate plaintext passwords.
      /// The LogonUser function does not cache credentials for this logon type.
      /// </summary>
      Network = 3 ,        
      /// <summary>
      /// This logon type is intended for batch servers, where processes may be executing on behalf of a user without
      /// their direct intervention. This type is also for higher performance servers that process many plaintext
      /// authentication attempts at a time, such as mail or Web servers.
      /// The LogonUser function does not cache credentials for this logon type.      
      /// </summary>
      Batch = 4,          
      /// <summary>
      /// Indicates a service-type logon. The account provided must have the service privilege enabled.
      /// </summary>
      Service = 5,
      /// <summary>
      /// This logon type is for GINA DLLs that log on users who will be interactively using the computer.
      /// This logon type can generate a unique audit record that shows when the workstation was unlocked. 
      /// </summary>
      Unlock = 7,
      /// <summary>
      /// This logon type preserves the name and password in the authentication package, which allows the server to make
      /// connections to other network servers while impersonating the client. A server can accept plaintext credentials
      /// from a client, call LogonUser, verify that the user can access the system across the network, and still
      /// communicate with other servers.
      /// NOTE: Windows NT:  This value is not supported.
      /// </summary>
      NetworkCleartext = 8,
      /// <summary>
      /// This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
      /// The new logon session has the same local identifier but uses different credentials for other network connections.
      /// NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
      /// NOTE: Windows NT:  This value is not supported. 
      /// </summary>
      NewCredentials = 9,  
    }

    /// <summary>
    /// Login Providers
    /// </summary>
    internal enum login32Provider
    {
      /// <summary>
      /// Default login provider for the machine
      /// </summary>
      Default = 0,
      /// <summary>
      /// Windows NT 3.5 login provider
      /// </summary>
      WINNT35 = 1,
      /// <summary>
      /// Windows NT 4.0 login provider
      /// </summary>
      WINNT40 = 2,
      /// <summary>
      /// Windows 2000 login provider
      /// </summary>
      WINNT50 = 3
    }

  }
}
